#pragma once

#include "msli/entities/Point.hpp"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
class BottomEntry {
public:
    Point<T> clusterCenter;     // 底层节点: 簇中心点
    DIS_TYPE radius{};          // 底层节点: 簇半径
    DIS_TYPE dis_lower{};

    std::vector<Point<T>> objects;      // 底层节点: 簇中的数据点

    BottomEntry() = default;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    BottomEntry(const Point<T> &center, const std::vector<Point<T>> &objs) : clusterCenter(center), objects(objs) {

        DIS_TYPE max_dis = 0.0, min_dis = std::numeric_limits<double>::max();
        for (int i = 0; i < objects.size(); i++) {
            DIS_TYPE dis = objects[i].distance(clusterCenter);
            if (max_dis < dis)
                max_dis = dis;

            if (min_dis > dis)
                min_dis = dis;
        }

        this->dis_lower = min_dis;
        this->radius = max_dis;
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 析构函数
    ~BottomEntry() {
        objects.clear();
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
class UpperEntry {
public:
    Point<T> pivotPoint;                                              // 上层节点: 枢轴点
    DIS_TYPE minDistToPivot{}, maxDistToPivot{};                      // 上层节点: 各底层簇心距枢轴点最近最远距离

    std::vector<DIS_TYPE> dis_upper;                                  // 上层节点: 各簇心到枢轴点的距离
    std::vector<DIS_TYPE> dis_lower;
    std::vector<BottomEntry<T> *> bottomEntryByMinDisToPivot;         // 上层节点: 存储距离相对应的底层簇心指针
    std::vector<BottomEntry<T> *> bottomEntryByMaxDisToPivot;

    std::vector<T> coefficients_upper;                                // 上层节点: 多项式拟合模型
    std::vector<T> coefficients_lower;

    UpperEntry() = default;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    UpperEntry(const Point<T> &pivot, const std::vector<BottomEntry<T> *> &bottomEntry) :
            pivotPoint(pivot), bottomEntryByMinDisToPivot(bottomEntry), bottomEntryByMaxDisToPivot(bottomEntry) {

        std::vector<DIS_TYPE> maxDistances;
        SortPointsByMaxDistance(maxDistances);

        std::vector<DIS_TYPE> minDistances;
        SortPointsByMinDistance(minDistances);

        std::vector<COEFF_TYPE> positionMap;
        BuildPositionMap(positionMap);

        PolynomialRegression<T> polynomialRegression;
        std::vector<COEFF_TYPE> coe_lower;
        std::vector<COEFF_TYPE> coe_upper;

        WritePivotMinMaxToFile("../../results/msli/pivotMinMaxArray.txt", minDistances, maxDistances);

        if (polynomialRegression.fitIt(minDistances, positionMap, COEFFICIENTS, coe_lower) &&
            polynomialRegression.fitIt(maxDistances, positionMap, COEFFICIENTS, coe_upper)) {
            coefficients_lower = std::move(coe_lower);
            coefficients_upper = std::move(coe_upper);
            dis_lower = std::move(minDistances);
            dis_upper = std::move(maxDistances);
            std::cout << "模型拟合成功！！！！！！！！！！！！！！！！！！" << std::endl;
        } else {
            throw std::runtime_error("Model training anomaly, please double check the data format");
        }
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 析构函数
    ~UpperEntry() {
        coefficients_lower.clear();
        coefficients_upper.clear();
        bottomEntryByMinDisToPivot.clear();
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void WritePivotMinMaxToFile(const std::string &filename, std::vector<DIS_TYPE> &minDistances,
                                std::vector<DIS_TYPE> &maxDistances) {
        std::ofstream outFile(filename, std::ios_base::app);
        if (outFile.is_open()) {
            PrintMinMax(outFile, minDistances, maxDistances);
            outFile.close();
        } else {
            std::cerr << "无法打开文件 " << filename << " 进行写入。" << std::endl;
        }
    }

    void PrintMinMax(std::ostream &os, std::vector<DIS_TYPE> &minDistances, std::vector<DIS_TYPE> &maxDistances) const {
        os << std::fixed << std::setprecision(6);

        os << "索引下标\t\t\t" << "minArr 中的簇ID\t\t" << "到枢轴点的 min 距离\t\t" << "maxArr 中的簇ID\t\t"
           << "到枢轴点的 max 距离" << std::endl;

        for (int i = 0; i < bottomEntryByMinDisToPivot.size(); ++i) {
            os << std::setw(12) << i << "\t\t" << std::setw(12) << bottomEntryByMinDisToPivot[i]->clusterCenter.id
               << "\t\t" << std::setw(12) << minDistances[i] << "\t\t" << std::setw(12)
               << bottomEntryByMaxDisToPivot[i]->clusterCenter.id << "\t\t" << std::setw(12)
               << maxDistances[i] << std::endl;
        }
        os << "**********************************************************************************" << std::endl;
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void SortPointsByMinDistance(std::vector<COEFF_TYPE> &minDistances) {

        std::sort(bottomEntryByMinDisToPivot.begin(), bottomEntryByMinDisToPivot.end(),
                  [this](const BottomEntry<T> *a, const BottomEntry<T> *b) {
                      DIS_TYPE distanceA = a->clusterCenter.distance(pivotPoint);
                      DIS_TYPE distanceB = b->clusterCenter.distance(pivotPoint);

                      if (distanceA != 0.0) {
                          distanceA = fabs(distanceA - a->radius);
                      }

                      if (distanceB != 0.0) {
                          distanceB = fabs(distanceB - b->radius);
                      }

                      return distanceA < distanceB;
                  });

        minDistances.clear();

        for (const auto &point: bottomEntryByMinDisToPivot) {
            DIS_TYPE distance = fabs(point->clusterCenter.distance(pivotPoint) - point->radius);

            if (pivotPoint.id == point->clusterCenter.id) {
                distance = point->dis_lower;
            }

            minDistances.push_back(distance);
        }

        this->minDistToPivot = minDistances[0];
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void SortPointsByMaxDistance(std::vector<COEFF_TYPE> &maxDistances) {
        std::sort(bottomEntryByMaxDisToPivot.begin(), bottomEntryByMaxDisToPivot.end(),
                  [this](const BottomEntry<T> *a, const BottomEntry<T> *b) {
                      DIS_TYPE distanceA = a->clusterCenter.distance(pivotPoint) + a->radius;
                      DIS_TYPE distanceB = b->clusterCenter.distance(pivotPoint) + b->radius;

                      return distanceA < distanceB;
                  });

        maxDistances.clear();

        for (const auto &point: bottomEntryByMaxDisToPivot) {
            DIS_TYPE distance = point->clusterCenter.distance(pivotPoint) + point->radius;

            maxDistances.push_back(distance);
        }

        this->maxDistToPivot = maxDistances[maxDistances.size() - 1];
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void BuildPositionMap(std::vector<COEFF_TYPE> &positionMap) {
        positionMap.clear();
        for (size_t i = 0; i < bottomEntryByMinDisToPivot.size(); ++i) {
            positionMap.push_back(static_cast<COEFF_TYPE>(i));
        }
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
class InterSecVector {
public:
    OID_TYPE oid;
    DIS_TYPE interDis_lower, interDis_upper;
    BottomEntry<T> *bottomEntry;
public:
    InterSecVector(OID_TYPE oid, DIS_TYPE interDisLower, DIS_TYPE interDisUpper, BottomEntry<T> *bottomEntry) : oid(
            oid), interDis_lower(interDisLower), interDis_upper(interDisUpper), bottomEntry(bottomEntry) {}

};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////